/**
 * 
 * 
 *  
 *====================================================
 * 文件名称: ActionFrame.java
 * 修订记录：
 * No    日期				作者(操作:具体内容)
 * 1.    20010-1-2			
 * 2.    2010-4-16          （增加对导出EXCEL的支持，简化EXCEL导出开发。）
 *====================================================
 * 类描述：(说明未实现或其它不应生成javadoc的内容)
 * 
 */
package com.devframe.web.struts1;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import org.apache.commons.beanutils.ConvertUtils;
import org.apache.log4j.Logger;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DispatchAction;

import com.devframe.exception.MyActionConfigException;
import com.devframe.exception.MyException;
import com.devframe.web.MyAction;
import com.devframe.web.MyContext;
import com.devframe.web.MyStatics;
import com.devframe.web.WebContext;
import com.devframe.web.handle.ExportXlsResultHandle;
import com.devframe.web.handle.JSONResultHandle;
import com.devframe.web.interceptors.InterceptAble;
import com.devframe.web.interceptors.MyInterceptorStack;
import com.devframe.web.loader.MyWebContextLoader;
import com.devframe.web.struts1.converters.BigDecimalConverter;
import com.devframe.web.struts1.converters.BigIntegerConverter;
import com.devframe.web.struts1.converters.DateConverter;
import com.devframe.web.struts1.converters.TimestampConverter;
import com.devframe.web.util.common.PagePathUtils;

/**
 * <pre>
 * &lt;b&gt;&lt;font color=&quot;blue&quot;&gt;ActionFrame&lt;/font&gt;&lt;/b&gt;
 * </pre>
 * 
 * <pre>
 * &lt;b&gt; --基础ACTION框架--&lt;/b&gt;
 * </pre>
 * <pre>
 *   在struts1的配置文件中将此类做action类进行配置，将符合要求的（继承MyAction）的类作为ActionForm进行配置。
 *   应用中可以继承该类实现不同的拦截器以满足特定的需求。
 *   可以简化Action类的编写。Action类将不需要和Servlet环境直接打交道，避免繁多的request.getParamter和request.setAttribute等操作。
 *   框架同时考虑了AJAX处理的便利性。
 * &lt;pre&gt;
 * &lt;b&gt;--样例--&lt;/b&gt;
 *   ActionFrame obj = new ActionFrame();
 * &lt;/pre&gt;
 * JDK版本：JDK1.4
 * &#064;author  &lt;b&gt;&lt;/b&gt;
 * 
 */
public class MyStruts1ActionFrame extends DispatchAction {
    /**
     * DEFAULT_PARAMETER默认方法参数名，mthod
     */
    private static final String DEFAULT_PARAMETER = "method";

    /**
     * DEFAULT_METHOD默认调用方法名execute
     */
    private static final String DEFAULT_METHOD = "execute";

    /**
     * LOG
     */
    private static final Logger LOG = Logger.getLogger(MyStruts1ActionFrame.class);
    

    static {
        // 注册一些转换器，避免ActionForm中转换出错
        ConvertUtils.register(new DateConverter(), java.util.Date.class);
        ConvertUtils.register(new TimestampConverter(), java.sql.Timestamp.class);
        ConvertUtils.register(new BigIntegerConverter(), java.math.BigInteger.class);
        ConvertUtils.register(new BigDecimalConverter(), java.math.BigDecimal.class);
    }

    /**
     * Action框架分发处理方法 实际执行的ACTION类不需要依赖于Servlet上下文，只需要简单的JAVA BEAN。
     * 每个Action方法都返回String表示结果，如果result以AJAX:开头，表示返回AJAX以及AJAX对应的属性列表。
     * 如果Action返回的对象不是String也作为AJAX处理。
     * 
     * @param mapping
     *            ActionMapping
     * @param form
     *            ActionForm
     * @param request
     *            HttpServletRequest
     * @param response
     *            HttpServletResponse
     * @param name
     *            name
     * @return ActionForward
     * @throws Exception
     *             Exception
     * 
     */
    protected ActionForward dispatchMethod(ActionMapping mapping, ActionForm form, HttpServletRequest request,
        HttpServletResponse response, String name) {
    	if (form instanceof MyAction) {
            // 创建上下文，注入action类实例
            MyContext context = new WebContext(request);
            MyAction action = (MyAction) form;
            action.setContext(context);
            // 用spring注入依赖在spring中托管的对象
            MyWebContextLoader.getInstance().autowireComponent(form);
            // 调用请求的方法，默认调用execute.返回字符串为相应的forward或者要转换成JSON的对象
            Method method=null;
            try {
                method = form.getClass().getMethod(name);
            } catch (Exception e) {
                e.printStackTrace();
                return processException(mapping, request, response, String.class, e);
            }
            // 判断调用方法的返回类型，如果不是String则当做ajax请求处理
            Class returnType = String.class;
            if(method!=null){
                returnType = method.getReturnType();
            }

            // 预处理拦截器
            MyInterceptorStack interceptorStack = null;
            if (action instanceof InterceptAble) {
                interceptorStack = ((InterceptAble) action).getInterceptorStack();
                if (interceptorStack != null) {
                    interceptorStack.before(context, action);
                }
            }
            try {
                Object actionResult = name;
                if(method!=null){
                    actionResult = method.invoke(form);
                }
                //支持void无返回值的action方法，返回页面为方法名
                if(actionResult==null){
                    actionResult = name;
                }
                // 后处理拦截器
                if (interceptorStack != null) {
                    interceptorStack.after(context, action, actionResult);
                }
                //如果方法有返回值且不为String类型或者void则断定为Ajax请求。
                if (!returnType.equals(void.class) && !returnType.equals(String.class)) {
                    processAjaxRequest(mapping, response, name, context, actionResult);
                    return null;
                } else {
                    return processNormalRequest(mapping, response, context, action, actionResult);
                }
            } catch (Throwable e) {
                return processException(mapping, request, response, returnType, e);
            }
        } else {// 配置有误
            String errMsg = "struts 配置错误,ActionForm 必须继承MyAction。";
            LOG.error(errMsg);
            request.setAttribute("message", errMsg);
            MyActionConfigException ex = new MyActionConfigException(errMsg);
            MyException de = new MyException(ex);
            request.setAttribute(MyException.GLOBAL_EXCEPTION_KEY, de);
            return mapping.findForward("exception");
        }
    }

    /**
     * processNormalRequest
     * @param mapping mapping
     * @param response response
     * @param context context
     * @param action action
     * @param actionResult actionResult
     * @return ActionForward
     * 如果是导出EXCEL请求则返回null
     * 
     */
    private ActionForward processNormalRequest(ActionMapping mapping, HttpServletResponse response,
        MyContext context, MyAction action, Object actionResult) {
        String forwardStr = (String) actionResult;
        if(forwardStr!=null && forwardStr.indexOf(MyStatics.REDIRECT_ACTION_RESULT_PREFIX)==0){
            ActionForward forward = new ActionForward();
            forward.setRedirect(true);
            forward.setPath(forwardStr.substring(9));
            return forward;
        }
        //判断是否包含专项指令：返回值为"redirect:"开头的字符串
        ActionForward forward = mapping.findForward(forwardStr);
        //找不到forward则按约定创建
        if(forward==null){      
        	String path=PagePathUtils.accessTypeAJspPagePath(action.getClass(), mapping.getPath(), actionResult.toString());
            LOG.info("自动生成forward:"+path);
            forward = new ActionForward(path);
        }
        
        // 判断是否为导出EXCEL请求,exportToExcel=true或者返回的模板为xls文件
        String forwardPath = forward.getPath();
        if (forwardPath.matches(".*\\.(xls|XLS)")) {
            exportExcel(context, action, response, forwardPath);
            return null;
        }
        if ("true".equals(context.getParameter(MyStatics.PARAM_EXPORT_TO_XLS))) {
            forwardPath = forwardPath.replaceAll("\\.(jsp|JSP)", ".xls");
            exportExcel(context, action, response, forwardPath);
            return null;
        }
        // 普通JSP
        return forward;
    }

    /**
     * processAjaxRequest
     * @param mapping mapping
     * @param response response
     * @param name name
     * @param context context
     * @param actionResult actionResult
     * @throws IOException IOException
     * 
     */
    private void processAjaxRequest(ActionMapping mapping, HttpServletResponse response, String name,
        MyContext context, Object data) throws IOException {
        // 判断是否为导出，对应AJAX的导出请求，必须有exportToExcel=true,此时将返回值传递给模板
        // 可以通过exportTemplate参数指定导出模板，默认模板为method对应的forward
        if ("true".equals(context.getParameter(MyStatics.PARAM_EXPORT_TO_XLS))) {
            String tmplateFile = context.getParameter(MyStatics.PARAM_EXPORT_XLS_FILE_NAME);
            ActionForward forward = mapping.findForward(name);
            if ((tmplateFile==null || tmplateFile.equals("")) && forward!=null) {
                tmplateFile = forward.getPath();
                tmplateFile = tmplateFile.replaceAll("\\.(jsp|JSP)", ".xls");
            }
            exportExcel(context, data, response, tmplateFile);
        } else{
            // 返回对象类型当做AJAX处理。
            JSONResultHandle jsonResult=new JSONResultHandle();
            jsonResult.execute(context, response, data);
            jsonResult=null;
        }
    }

    /**
     * processException
     * 异常处理
     * @param mapping mapping
     * @param request request
     * @param response  response
     * @param returnType returnType
     * @param e e
     * @return ActionForward
     * @throws IOException IOException
     * 
     */
    private ActionForward processException(ActionMapping mapping, HttpServletRequest request,
        HttpServletResponse response, Class returnType, Throwable e){
        Throwable cause = getRootCause(e);
        if (cause == null) {
            cause = e;
        }
        if (!returnType.equals(void.class) && !returnType.equals(String.class)) {
            LOG.error("AJAX请求处理出错：" + cause.getLocalizedMessage());
            cause.printStackTrace();
            JSONResultHandle jsonResult=new JSONResultHandle();
            Map error = new HashMap();
            error.put("status", "error");
            error.put("message", cause.getLocalizedMessage());
            error.put("errorStack", cause.getStackTrace());
            jsonResult.execute(response, error);
            jsonResult=null;
            return null;
        } else {
            LOG.error("action处理出错：" + cause.getLocalizedMessage());
            cause.printStackTrace();
            MyException de = new MyException(cause);
            request.setAttribute(MyException.GLOBAL_EXCEPTION_KEY, de);
            return mapping.findForward("exception");
        }
    }
    
    /**
     * getRootCause
     * 获取异常的原始异常，用于反馈异常的真实原因。
     * @param ex 异常信息
     * @return 原始异常，最底层异常
     * 
     */
    private Throwable getRootCause(Throwable ex){
        Throwable cause = ex;
        while(cause.getCause()!=null){
            cause = cause.getCause();
        }
        return cause;
    }

    /**
     * ACTION框架执行方法 处理默认方法参数以及默认方法
     * 
     * @param mapping
     *            ActionMapping
     * @param form
     *            ActionForm
     * @param request
     *            HttpServletRequest
     * @param response
     *            HttpServletResponse
     * @return ActionForward
     * @throws Exception
     *             Exception
     * 
     */
    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
        HttpServletResponse response) throws Exception {
        String parameter = mapping.getParameter();
        if (parameter == null) {// 默认方法参数method
            parameter = DEFAULT_PARAMETER;
        }
        String name = request.getParameter(parameter);
        if (name == null || name.equals("")) {// 默认调用execute方法
            name = DEFAULT_METHOD;
        }
        try {
            return dispatchMethod(mapping, form, request, response, name);
        } catch (Exception e) {
            e.printStackTrace();
            MyException de = new MyException(e);
            request.setAttribute(MyException.GLOBAL_EXCEPTION_KEY, de);
            return mapping.findForward("exception");
        }
    }

    /**
     * 导出EXCEL文件 当返回模板为excel文件时表示导出EXCEL
     * 
     * @param context
     *            context
     * @param obj
     *            Object
     * @param response
     *            response
     * @param templateFileName
     *            templateFileName
     * 
     */
    public void exportExcel(MyContext context, Object data, HttpServletResponse response, String templateFileName) {
       LOG.info("导出EXCEL文件");
       ExportXlsResultHandle xlsResult=new ExportXlsResultHandle();
       xlsResult.execute(context, response, data, templateFileName);
       xlsResult=null;
    }

}
